// LSL script generated: RLV-New.Addons.DV_MAIN_LEASH.lslp Sun May  2 00:09:51 Westeuropäische Sommerzeit 2010
//leash script for the Open Collar Project (c)
//Licensed under the GPLv2, with the additional requirement that these scripts remain "full perms" in Second Life.  See "OpenCollar License" for details.




//Here All Other Varibales
integer targethandle;
vector pos = ZERO_VECTOR;
integer stay = 0;
integer listenhandle;
float length = 3.0;
string part_texture = "chain";
key part_target = NULL_KEY;
key leasher = NULL_KEY;

integer lastrank;
integer stayrank;
// string dbtoken = "leashtexture"; a more general dbtoken is needed as we now save not only leash texture
string dbtoken = "leash";
list leashSettings;

//help
string parentmenu = "Main";
string submenu = "Leash";
integer menulistener;
integer menuchannel = -1908789;
string currentmenu = "";

//string UPMENU = "↑";
//string MORE = "→";
string UPMENU = "Back";
string LEASH = "Grab";
string UNLEASH = "Unleash";
string STAY = "Stay";
string UNSTAY = "UnStay";
string L_LENGTH = "Length";
string L_YANK = "Yank";
list postbuttons;
list postkeys;
string postprompt;
string menuuser;
integer postlistener;
integer danglecount = -1;
integer menucount = -1;
key leash_holder;

string sensormode;
key keyHolder;
list lengths = ["1","2","3","4","5","8","10","15","20","25","30"];

debug(string str){
}

LeashMenu(key id){
    (currentmenu = "leash");
    list buttons;
    (buttons += [LEASH,L_YANK,L_LENGTH]);
    (buttons += [" ",UNLEASH," "]);
    (buttons += [STAY,UNSTAY,UPMENU]);
    (buttons = RestackMenu(buttons));
    string prompt = "Leash Options";
    (prompt += "\n(Menu will time out in 60 seconds.)");
    (menulistener = llListen(menuchannel,"",id,""));
    (menucount = 60);
    llSetTimerEvent(1.0);
    llDialog(id,prompt,buttons,menuchannel);
}

LengthMenu(key id){
    (currentmenu = "length");
    list buttons;
    (buttons += lengths);
    (buttons += [UPMENU]);
    (buttons = RestackMenu(FillMenu(buttons)));
    string prompt = "Set a leash length in meter:";
    (prompt += "\n(Menu will time out in 60 seconds.)");
    (menulistener = llListen(menuchannel,"",id,""));
    (menucount = 60);
    llSetTimerEvent(1.0);
    llDialog(id,prompt,buttons,menuchannel);
}

list FillMenu(list in){
    while (((((llGetListLength(in) != 3) && (llGetListLength(in) != 6)) && (llGetListLength(in) != 9)) && (llGetListLength(in) < 12))) {
        (in += [" "]);
    }
    return in;
}

list RestackMenu(list in){
    list out = llList2List(in,9,11);
    (out += llList2List(in,6,8));
    (out += llList2List(in,3,5));
    (out += llList2List(in,0,2));
    return out;
}

SetTexture(string newtex){
    (part_texture = newtex);
    string message = ("Leash texture set to " + part_texture);
    if ((leasher != NULL_KEY)) llInstantMessage(leasher,message);
    llOwnerSay(message);
    if ((llListFindList(leashSettings,["texture"]) == -1)) {
        (leashSettings += ["texture",((string)part_texture)]);
    }
    else  {
        integer index = (llListFindList(leashSettings,["texture"]) + 1);
        (leashSettings = llListReplaceList(leashSettings,[((string)part_texture)],index,index));
    }
    llMessageLinked(-4,2000,((dbtoken + "=") + llDumpList2String(leashSettings,",")),NULL_KEY);
}

SetLength(float newlength,key id){
    (length = newlength);
    if ((leasher != NULL_KEY)) {
        llTargetRemove(targethandle);
        (targethandle = llTarget(pos,length));
    }
    string message = ("Leash length set to " + ((string)length));
    if ((id != llGetOwner()));
    {
        llOwnerSay(message);
    }
    llInstantMessage(id,message);
}

LeashTo(key holder,integer rank,list leashpoints){
    (danglecount = -1);
    llListenRemove(listenhandle);
    (leash_holder = NULL_KEY);
    (lastrank = rank);
    integer leashpointcount = llGetListLength(leashpoints);
    if (leashpointcount) {
        string listenfor = "";
        if ((leashpointcount == 1)) {
            (listenfor = ((((string)holder) + llList2String(leashpoints,0)) + " ok"));
        }
        (listenhandle = llListen(-8888,"",NULL_KEY,listenfor));
    }
    (leasher = holder);
    (part_target = leasher);
    LeashParticles(part_target);
    debug(("leasher: " + ((string)leasher)));
    (pos = llList2Vector(llGetObjectDetails(leasher,[3]),0));
    llTargetRemove(targethandle);
    llStopMoveToTarget();
    (targethandle = llTarget(pos,length));
    llMoveToTarget(pos,1.0);
    integer n;
    debug(("leashpoints: " + ((string)leashpointcount)));
    for ((n = 0); (n < leashpointcount); (n++)) {
        llSay(-8888,(((string)holder) + llList2String(leashpoints,n)));
    }
}

StayPut(key id,integer rank){
    if ((leasher != NULL_KEY)) {
        Unleash();
    }
    (stayrank = rank);
    (stay = 1);
    llRequestPermissions(llGetOwner(),4);
    llOwnerSay((llKey2Name(id) + " commanded you to stay in place, you cannot move until the command is revoked again."));
    llInstantMessage(id,(("You commanded " + llKey2Name(llGetOwner())) + " to stay in place, to allow to move again, either leash the slave with the grab command or use unstay to enable movement again."));
}

Unleash(){
    llStopMoveToTarget();
    (danglecount = -1);
    llListenRemove(listenhandle);
    llTargetRemove(targethandle);
    llParticleSystem([]);
    (leasher = NULL_KEY);
    (leash_holder = NULL_KEY);
    (part_target = NULL_KEY);
    (lastrank = 504);
}

LeashParticles(key target){
    debug(("sending particles to " + ((string)target)));
    string texture = part_texture;
    integer flags = 0;
    (flags = (flags | 256));
    (flags = (flags | 16));
    (flags = (flags | 32));
    integer pattern = 1;
    if ((target != NULL_KEY)) (flags = (flags | 64));
    list sys = [7,6.0,1,<1.0,1.0,1.0>,3,<0.0,0.0,1.0>,2,1.0,4,0.5,5,<6.999999999999999e-2,6.999999999999999e-2,0.1>,6,<0.1,0.1,0.1>,13,3.0e-2,15,1,16,0.1,17,1.0,18,1.0,8,<0.0,0.0,-0.4>,22,0.0,23,3.14159274,21,<0.0,0.0,1.0>,19,0.0,0,flags,9,pattern,20,target,12,texture];
    llParticleSystem(sys);
}

integer isInSim(vector v){
    if ((((((v == ZERO_VECTOR) || (v.x < 0)) || (v.x > 256)) || (v.y < 0)) || (v.y > 256))) return 0;
    else  return 1;
}

YankTo(key id){
    vector dest = ((vector)llList2String(llGetObjectDetails(id,[3]),0));
    llMoveToTarget(dest,0.5);
    llSleep(2.0);
    llStopMoveToTarget();
}

default {

    state_entry() {
        Unleash();
        (menuchannel = ((-((integer)llFrand(999950.0))) - 99999));
        llSleep(1.0);
    }

    
    on_rez(integer param) {
        llResetScript();
    }

    
    link_message(integer sender,integer auth,string str,key id) {
        if ((auth == 1337)) {
            if ((str == llGetScriptName())) {
                llMessageLinked(-4,1338,llGetScriptName(),NULL_KEY);
                (keyHolder = id);
                LeashMenu(id);
            }
        }
        if ((auth == 1338)) {
            if ((str != llGetScriptName())) {
                llListenRemove(menulistener);
            }
        }
        if (((auth >= 500) && (auth <= 503))) {
            list params = llParseString2List(str,[" "],[]);
            string command = llList2String(params,0);
            if ((((str == "grab") || (str == "leash")) && (id != llGetOwner()))) {
                if ((leasher != NULL_KEY)) {
                    LeashTo(id,auth,["handle"]);
                }
            }
            else  if ((str == "unleash")) {
                if (((id == leasher) || (auth <= lastrank))) Unleash();
            }
            else  if (((str == "yank") && (id == leasher))) {
                YankTo(id);
            }
            else  if ((llList2String(params,0) == "length")) {
                float newlength = ((float)llList2String(params,1));
                if ((newlength > 0.0)) {
                    if ((leasher == NULL_KEY)) {
                        SetLength(newlength,id);
                    }
                    else  if ((id == leasher)) {
                        SetLength(newlength,id);
                    }
                    else  if ((auth <= lastrank)) {
                        SetLength(newlength,id);
                    }
                }
                else  {
                    if ((id != llGetOwner())) {
                        llOwnerSay((("The current leash length is " + ((string)length)) + "m."));
                    }
                    llInstantMessage(id,(("The current leash length is " + ((string)length)) + "m."));
                }
            }
            else  if ((str == "reset")) {
                if (((auth == 503) || (auth == 500))) {
                    llResetScript();
                }
            }
            else  if ((str == "post")) {
                if (((auth >= 500) && (auth <= 503))) {
                    (menuuser = id);
                    (sensormode = "post");
                    llSensor("",NULL_KEY,8,10.0,3.14159274);
                }
            }
            else  if ((command == "post")) {
                if (((auth <= lastrank) || (lastrank == 0))) {
                    LeashTo(((key)llList2String(params,1)),auth,[]);
                }
                else  {
                    string wearer = llKey2Name(llGetOwner());
                    string swearer = llList2String(llParseString2List(llKey2Name(llGetOwner()),[" "],[]),0);
                    llInstantMessage(id,(((("Sorry, someone who outranks you on " + wearer) + "'s collar leashed ") + swearer) + " already."));
                }
            }
            else  if ((str == "stay")) {
                if (((auth >= 500) && (auth <= 502))) StayPut(id,auth);
            }
            else  if ((((str == "unstay") || (str == "move")) && stay)) {
                if ((auth <= stayrank)) {
                    (stay = 0);
                    llReleaseControls();
                    llOwnerSay("You are free to move again.");
                    llInstantMessage(id,(("You allowed " + llKey2Name(llGetOwner())) + " to move freely again."));
                }
            }
            else  if ((llGetInventoryType(str) == 0)) {
                if ((leasher == NULL_KEY)) {
                    SetTexture(str);
                }
                else  if ((id == leasher)) {
                    SetTexture(str);
                    LeashParticles(part_target);
                }
                else  if ((auth <= lastrank)) {
                    SetTexture(str);
                    LeashParticles(part_target);
                }
            }
        }
        else  if ((auth == 504)) {
            if ((id == leasher)) {
                if ((str == "unleash")) {
                    Unleash();
                }
                else  if ((str == "yank")) {
                    YankTo(id);
                }
            }
        }
        else  if ((((auth == 3002) && (str == submenu)) || ((auth == 150385) && (str == "LeashMenu")))) {
            if ((id != llGetOwner())) {
                (keyHolder = id);
                LeashMenu(id);
            }
        }
    }


    sensor(integer num) {
        if ((num > 12)) {
            (num = 12);
        }
        if ((sensormode == "post")) {
            (postbuttons = []);
            (postprompt = "Pick the object on which you want the sub to be leashed to.  If it's not in the list, have the sub move closer and try again.\n");
            (postkeys = []);
            integer n;
            for ((n = 0); (n < num); (n++)) {
                string name = llDetectedName(n);
                if ((name != "Object")) {
                    (postbuttons += [((string)(n + 1))]);
                    if ((llStringLength(name) > 44)) {
                        (name = (llGetSubString(name,0,40) + "..."));
                    }
                    (postprompt += ((("\n" + ((string)(n + 1))) + " - ") + name));
                    (postkeys += [llDetectedKey(n)]);
                }
            }
            while ((llStringLength(postprompt) >= 512)) {
                (postbuttons = llDeleteSubList(postbuttons,-1,-1));
                (postkeys = llDeleteSubList(postkeys,-1,-1));
                (postprompt = llDumpList2String(llDeleteSubList(llParseString2List(postprompt,["\n"],[]),-1,-1),"\n"));
            }
            (postbuttons = RestackMenu(FillMenu(postbuttons)));
            (postlistener = llListen(menuchannel,"",menuuser,""));
            (menucount = 60);
            llSetTimerEvent(1.0);
            (currentmenu = "post");
            llDialog(menuuser,postprompt,postbuttons,menuchannel);
        }
    }

    
    no_sensor() {
        llInstantMessage(menuuser,"Unable to find targets to leash to.");
    }

    at_target(integer num,vector position,vector ourpos) {
        llStopMoveToTarget();
        llTargetRemove(targethandle);
        (pos = llList2Vector(llGetObjectDetails(leasher,[3]),0));
        (targethandle = llTarget(pos,length));
        if ((leasher != part_target)) {
            if ((llKey2Name(part_target) == "")) {
                (part_target = leasher);
                LeashParticles(part_target);
            }
        }
        else  if ((llKey2Name(leash_holder) != "")) {
            debug("test1");
            (part_target = leash_holder);
            LeashParticles(part_target);
        }
    }

    timer() {
        if ((leasher != NULL_KEY)) {
            vector newpos = llList2Vector(llGetObjectDetails(leasher,[3]),0);
            if (((!isInSim(newpos)) && (danglecount == -1))) {
                llStopMoveToTarget();
                llTargetRemove(targethandle);
                llParticleSystem([]);
                llListenRemove(listenhandle);
                (danglecount = 120);
            }
            else  if ((isInSim(newpos) && (danglecount != -1))) {
                llStopMoveToTarget();
                LeashParticles(part_target);
                llTargetRemove(targethandle);
                (targethandle = llTarget(newpos,length));
                llMoveToTarget(newpos,1.0);
                (danglecount = -1);
            }
        }
        if (((((menucount == 0) && (danglecount == 0)) || ((menucount == 0) && (danglecount == -1))) || ((menucount == -1) && (danglecount == 0)))) {
            llSetTimerEvent(0.0);
        }
        if ((danglecount == 0)) {
            (danglecount = -1);
        }
        else  if ((danglecount > 0)) (danglecount--);
        if ((menucount == 0)) {
            (menucount = -1);
            llListenRemove(menulistener);
        }
        else  if ((menucount > 0)) (menucount--);
    }

    not_at_target() {
        vector newpos;
        (newpos = llList2Vector(llGetObjectDetails(leasher,[3]),0));
        if ((pos != newpos)) {
            (pos = newpos);
            llTargetRemove(targethandle);
            (targethandle = llTarget(pos,length));
        }
        llMoveToTarget(pos,0.7);
        if (((!isInSim(newpos)) && (danglecount == -1))) {
            llStopMoveToTarget();
            llTargetRemove(targethandle);
            (danglecount = 120);
            llParticleSystem([]);
            llSetTimerEvent(1.0);
        }
        if ((leasher != part_target)) {
            if ((llKey2Name(part_target) == "")) {
                (part_target = leasher);
                LeashParticles(part_target);
            }
        }
        else  if ((llKey2Name(leash_holder) != "")) {
            (part_target = leash_holder);
            LeashParticles(part_target);
        }
    }

    changed(integer change) {
        if ((change & 512)) {
            if ((leasher != NULL_KEY)) {
                llSleep(3.0);
                vector newpos;
                (newpos = llList2Vector(llGetObjectDetails(leasher,[3]),0));
                if ((newpos != ZERO_VECTOR)) {
                    LeashParticles(part_target);
                    llTargetRemove(targethandle);
                    (targethandle = llTarget(pos,length));
                }
            }
        }
    }

             
    listen(integer channel,string name,key id,string message) {
        if ((channel == -8888)) {
            if ((llGetOwnerKey(id) == leasher)) {
                (leash_holder = id);
                (part_target = id);
                LeashParticles(part_target);
            }
        }
        if (((id == keyHolder) && (id != llGetOwner()))) {
            if ((channel == menuchannel)) {
                llListenRemove(menulistener);
                (menuuser = id);
                if ((message == "Back")) {
                    llMessageLinked(-4,1337,"DV_BASIC",menuuser);
                }
                if ((message == L_LENGTH)) {
                    LengthMenu(id);
                }
                else  if ((message == "Grab")) {
                    LeashTo(id,500,["handle"]);
                }
                else  if ((message == "Unleash")) {
                    Unleash();
                }
                else  if ((message == "Stay")) {
                    StayPut(id,500);
                }
                else  if ((message == "UnStay")) {
                    (stay = 0);
                    llReleaseControls();
                }
                else  if ((message == "Yank")) {
                    YankTo(id);
                }
                else  if ((message == UPMENU)) {
                    if ((currentmenu == "length")) {
                        LeashMenu(id);
                    }
                    else  {
                        llMessageLinked(-4,3002,parentmenu,id);
                    }
                }
                else  if ((currentmenu == "post")) {
                    integer postnum = (((integer)message) - 1);
                    if ((postnum >= 0)) {
                        llMessageLinked(-4,0,("post " + llList2String(postkeys,postnum)),id);
                    }
                }
                else  if ((llListFindList(lengths,[message]) != -1)) {
                    llMessageLinked(-4,0,("length " + message),id);
                    LeashMenu(id);
                }
            }
        }
        else  {
            llMessageLinked(-4,0,llToLower(message),id);
        }
        (menucount = -1);
    }

    
    
    run_time_permissions(integer perm) {
        if ((4 & perm)) {
            llTakeControls(1342178048,0,0);
        }
    }
}
